#define SOLAR_BUILD_RECEIVER
#include <algorithm/analytical_model/algorithm/cuda/Utils.h>
#include <algorithm/analytical_model/algorithm/cuda/receiver/Receiver.cuh>

#include <cuda_runtime_api.h>

using namespace solar::cuda;

SOLAR_CUDA_INLINE __host__ solar::cuda::Receiver::Receiver(cudaStream_t stream)
{
    if (stream != nullptr)
    {
        this->external_stream_ = true;
        this->stream_ = stream;
    }
    else
    {
        cudaStreamCreate(&stream_);
    }
}

SOLAR_CUDA_INLINE __device__ __host__ solar::cuda::Receiver::Receiver(const Receiver& rec)
    : type_(rec.type_), normal_(rec.normal_), pos_(rec.pos_), size_(rec.size_),
      surface_index_(rec.surface_index_), pixel_length_(rec.pixel_length_), d_image_(rec.d_image_),
      resolution_(rec.resolution_), d_total_reflect_(rec.d_total_reflect_), stream_(rec.stream_),
      external_stream_(rec.external_stream_)
{
}

SOLAR_CUDA_INLINE __device__ __host__ solar::cuda::Receiver::~Receiver()
{
    d_image_ = nullptr;
    d_total_reflect_ = nullptr;
    stream_ = nullptr;
}

SOLAR_CUDA_INLINE void solar::cuda::Receiver::clear()
{
    if (d_image_ != nullptr)
    {
        CHECK_CUDA_ERRORS(cudaFreeAsync(d_image_, stream_));
        d_image_ = nullptr;
    }
    if (d_total_reflect_ != nullptr)
    {
        CHECK_CUDA_ERRORS(cudaFreeAsync(d_total_reflect_, stream_));
        d_total_reflect_ = nullptr;
    }
    if (!external_stream_)
    {
        cudaStreamDestroy(stream_);
    }
}

SOLAR_CUDA_FORCE_INLINE void solar::cuda::Receiver::allocImage()
{
    CHECK_CUDA_ERRORS(cudaMallocAsync(std::addressof(d_image_), sizeof(float) * n_resolution_, stream_));
    CHECK_CUDA_ERRORS(cudaMallocAsync(std::addressof(d_total_reflect_), sizeof(float) * 1024 * 125 * 160, stream_));
}

SOLAR_CUDA_INLINE void solar::cuda::Receiver::cleanImageContent() const
{
    CHECK_CUDA_ERRORS(cudaMemsetAsync(d_image_, 0, sizeof(float) * n_resolution_, stream_));
}
